import os
import sys
import time
import tkinter as tk
import threading
import shutil
import pyttsx3
from datetime import datetime
from tkinter import Listbox, Toplevel, END
from tkinter import filedialog, messagebox,ttk, simpledialog
from tkinter.scrolledtext import ScrolledText
from PIL import Image, ImageTk
from moviepy import ImageClip, AudioFileClip, TextClip, VideoFileClip, concatenate_videoclips, CompositeVideoClip,VideoClip

def picname2text(pic_folder):
    """
    遍历指定文件夹，获取每个文件的名称、绝对路径和文件类别（后缀）
     :return: 整体是文件名列表，每个元素为各文件名的字典 {序号, 绝对路径, 文件名}
    """
    file_names = []
    # 遍历文件夹中的所有文件
    for filename in os.listdir(pic_folder):
        if filename.lower().endswith((".jpg", ".jpeg", ".png",'gif', ".mp4", ".mov", ".avi")):
            file_names.append(filename)
    file_names.sort(key=lambda x: x[0])
    pic_names = [filename.split('.')[1] for filename in file_names]

    return pic_names

# 用空行为隔，将文本文件按照段落转换为语句列表，用于后面的语音和字幕
def text2list(textfile):
    # 逐段读取文本文件
    with open(textfile, 'r', encoding='utf-8') as file:
        current_paragraph = []
        text_list = []
        for line in file:
            if line.strip() == '':  # 如果遇到空行，认为是一个段落的结束
                if current_paragraph:  # 如果当前段落非空，则添加到列表中
                    text_list.append(''.join(current_paragraph))
                    current_paragraph = []  # 重置当前段落列表
            else:
                current_paragraph.append(line.strip())  # 添加到当前段落列表中
        if current_paragraph:  # 添加最后一个非空段落（如果存在）
            text_list.append(''.join(current_paragraph))
    return text_list

# 定义一个函数，将文本文件转换为音频文件
def text_to_audio(textfile, output_folder):
    # 初始化语音引擎
    engine = pyttsx3.init()
    # 设置语音速度为140
    engine.setProperty('rate', 130)
    # 设置语音音量为0.9
    engine.setProperty('volume', 1)
    # 设置语音为中文
    engine.setProperty('voice', 'zh-CN')
    # 创建一个空列表，用于存储音频文件路径
    audio_files = []
    # # 逐行读取文本文件
    # with open(textfile, 'r', encoding='utf-8') as file:
    #     # 读取文本文件中的每一行，并去除空行
    #     text_list = [line.strip() for line in file.readlines() if line.strip()]

    # 遍历文本列表
    for i, text in enumerate(textfile):
        # 构造音频文件路径
        audio_path = os.path.join(output_folder, f"slide_{i}.mp3")
        # 如果文本为空，朗读一个空格，生成一个极短音频作为占位
        if not text.strip():
            # 如果为空页，朗读一个空格，生成一个极短音频作为占位
            engine.save_to_file(" ", audio_path)
        else:
            engine.save_to_file(text, audio_path)
        # 将音频文件路径添加到列表中
        audio_files.append(audio_path)
    # 播放音频文件
    engine.runAndWait()
    # 返回音频文件路径列表
    return audio_files

def change_pinture(input_folder, output_folder,background_image):
    # 创建输出文件夹如果不存在
    os.makedirs(output_folder, exist_ok=True)

    # 遍历输入文件夹中的所有图片
    for filename in os.listdir(input_folder):
        if filename.lower().endswith(('.jpg','.png','gif','.jpeg')):
            # 打开原始图片
            original_image = Image.open(os.path.join(input_folder, filename))

            # 加载背景图（可以是任意格式的图片）
            # background_image_path = r"C:\Users\hexq\PycharmProjects\mypy\video\qiheqixiangjv\背景图片1.jpg"
            background = Image.open(background_image)  # .convert("RGBA")  # 使用 RGBA 以支持透明叠加
            background_size = (1280, 800)
            background = background.resize(background_size)
            # 设置最大尺寸为背景图的 80%，也可以根据需求调整
            max_overlay_width = int(background_size[0] * 0.8)
            max_overlay_height = int(background_size[1] * 0.8)
            # 定义目标尺寸,等比缩放图片以适应目标尺寸
            target_size = (max_overlay_width, max_overlay_height)
            original_image.thumbnail(target_size)
            #再次获取尺寸
            ov_w, ov_h = original_image.size
            # 计算粘贴的位置（左上角坐标）
            position = (
                (background_size[0] - ov_w) // 2,
                70 + int(background_size[1] - ov_h) // 2
            )

            # 将缩放后的图片粘贴到背景图上
            background.paste(original_image, position)

            # 保存结果（转换为RGB以避免保存为带透明通道的PNG）
            background.convert("RGB").save(os.path.join(output_folder, filename))
        elif filename.lower().endswith((".mp4", ".mov", ".avi")):
            shutil.copyfile(os.path.join(input_folder, filename), os.path.join(output_folder, filename))

def get_media_files(media_folder):
    # 定义一个函数，用于获取指定文件夹中的媒体文件
    media_files = []
    # 创建一个空列表，用于存储媒体文件
    for f in os.listdir(media_folder):
        # 遍历指定文件夹中的所有文件
        if f.lower().endswith((".jpg", ".jpeg", ".png", ".mp4", ".mov", ".avi")):
            # 判断文件是否为媒体文件
            media_files.append(os.path.join(media_folder, f))
            # 将媒体文件添加到列表中
    def extract_number(path):
        # 定义一个函数，用于提取文件名中的数字
        # return int(''.join(filter(str.isdigit, os.path.basename(path).split('.')[0])) or 0)
        return int(''.join([c for c in os.path.basename(path).split('.')[0] if c.isdigit()]) or 0)
        # 提取文件名中的数字，并将其转换为整数
    return sorted(media_files, key=extract_number)

def create_typing_effect(
    text,
    start_time,
    duration,
    fontsize=60,
    font="C:/Windows/Fonts/simhei.ttf",
    max_width=1024,

):

    text_clips = []  # 存储生成的字幕片段
    current_line = ""  # 当前正在构建的行
    lines = []  # 存储已完成的整行文本
    # 音频总时长除以字数=每个字的播报时间
    word_duration = duration / (len(text)+5) if len(text) > 0 else 0.1  # 增加5个字用来代替标点符号

    for i, char in enumerate(text):
        current_line += char
        # 测试当前行是否超出最大宽度
        test_clip = TextClip(
            text=current_line + "X",  # 加个 X 避免截断判断不准
            font_size=fontsize,
            font=font,
            size=(max_width, None),
            method='caption'   # 使用 caption 来实现自动换行 + 固定布局
        )

        # 如果当前行超出宽度，则换行
        if test_clip.w > max_width:
            current_line = current_line[:-1]  # 去掉最后一个字符
            lines.append(current_line)
            new_line = char
            current_line = new_line

        # 构建当前完整显示的文本（包含历史行）
        full_text = "\n".join(lines + [current_line])

        # 创建 TextClip
        text_clip = TextClip(
            text=full_text,
            font_size=fontsize,
            color='white',
            bg_color=(0, 0, 0, 0),
            font=font,
            size=(max_width, None),  # 设置最大宽度，自动换行
            method='caption',  # 自动换行 + 固定布局
            # stroke_color = 'black',
            # stroke_width = 3,
        ).with_position(("center", "top")) \
                .with_start(start_time + i * word_duration) \
                .with_duration(word_duration)  #  从左边开始显示

        text_clips.append(text_clip)
    return text_clips



def create_video_from_images_and_audios(textfile,media_files, audio_files, output_video, title_content, pause_duration=1.0):
    # 创建一个空列表，用于存储视频片段
    video_clips = []
    # 创建一个空列表，用于存储字幕片段
    subtitles = []
    titles = []
    # 初始化当前时间为0.0
    current_time = 0.000
    # # 逐行读取文本内容，用于生成字幕
    # with open(textfile, 'r', encoding='utf-8') as f:
    #     # 将文本内容按行读取，并去除空行
    #     text_lines = [line.strip() for line in f.readlines() if line.strip()]

    # ========== 创建封面页 ==========
    cover_image_path = r"C:\Users\hexq\PycharmProjects\mypy\video\qiheqixiangjv\封面图片.jpg"
    cover_duration = 3  # 封面页显示5秒

    # 创建封面页视频片段
    cover_clip = ImageClip(cover_image_path).resized((1280, 800)) \
        .with_duration(cover_duration) \
        .with_fps(24)

    # 创建封面字幕
    cover_subtitle = TextClip(
        text=title_content,
        font_size=80,
        color='yellow',
        bg_color=(0, 0, 0, 0),
        font=r"C:\Windows\Fonts\msyhbd.ttc",
        # stroke_color='red',
        # stroke_width=2
    ).with_position(("center", 200)) \
        .with_start(current_time) \
        .with_duration(cover_duration)
    # 创建封面标题
    cover_title = TextClip(
        text="齐河县气象局",
        font_size=48,
        color='white',
        bg_color=(0, 0, 0, 0),
        font=r"C:\Windows\Fonts\msyhbd.ttc"
    ).with_position(("center", 500)) \
        .with_start(current_time) \
        .with_duration(cover_duration)
    # 添加封面页和封面字幕到列表中
    video_clips.append(cover_clip)
    subtitles.append(cover_subtitle)
    titles.append(cover_title)

    # 设置后续普通页面的起始时间
    current_time = cover_duration
    text_lines = textfile

    print(media_files)
    print(audio_files)

    # 遍历图片文件和音频文件
    for i, (media_file, audio_file) in enumerate(zip(media_files, audio_files)):
        # 判断是图片还是视频
        ext = os.path.splitext(media_file)[1].lower()
        is_image = ext in [".jpg", ".jpeg", ".png"]
        is_video = ext in [".mp4", ".mov", ".avi"]

        # 读取已有的音频文件片段并获取时长
        audio_clip = AudioFileClip(audio_file)
        audio_duration = audio_clip.duration
        # 设置每页总时长 = 音频时长 + 停顿时长
        # pause_duration = 2.0  #  可调整
        total_duration = audio_duration + pause_duration

        # 构建媒体片段
        if is_image:
            media_clip = ImageClip(media_file).resized((1280, 800)).with_duration(total_duration).with_audio(audio_clip).with_fps(24)

        elif is_video:

            media_clip = VideoFileClip(media_file).without_audio()

            if media_clip.duration is None or media_clip.duration < total_duration:

                media_clip = media_clip.with_duration(total_duration).with_audio(audio_clip).with_fps(24)
                media_clip = media_clip.fx(vfx.freeze, t='end',
                                           freeze_duration=total_duration - (media_clip.duration or 0))
            else:
                media_clip = media_clip.with_duration(media_clip.duration).with_audio(audio_clip).with_fps(24)

        else:
            continue
        # 设置音频、视频的最长时长
        clip_duration = max(media_clip.duration, total_duration)
        # 将视频片段添加到列表中
        video_clips.append(media_clip)

        # 创建逐字显示的字幕片段
        if i < len(text_lines):
            text = text_lines[i]
            #调用 create_typing_effect 函数，生成逐字显示的字幕片段
            typing_clips = create_typing_effect(
                text=text,
                start_time=current_time,
                fontsize=60,
                font="C:/Windows/Fonts/msyhbd.ttc",
                max_width=1024,
                duration=audio_duration,

                # left_margin = 30,  # 可选：文字离左边距离
                # bottom_offset=50,  # 设置字幕离底部的距离（单位：像素）

            )

            # 将字幕片段添加到列表中
            subtitles.extend(typing_clips)


        # 更新当前时间
        current_time += clip_duration

    # 合成最终视频（按顺序拼接）
    final_video_clip = concatenate_videoclips(video_clips, method="chain")
    # 将字幕叠加到视频上
    final_video_with_subtitles = CompositeVideoClip([final_video_clip] + subtitles + titles)
    # 导出视频
    final_video_with_subtitles.write_videofile(output_video, codec="libx264", audio_codec="libmp3lame", fps=24)


def main_gui( media_folder, output_dir, title_content, pause_duration):
    def cleanup_temp_files(*folders):
        """
        删除指定的临时文件夹及其内容
        """
        for folder in folders:
            if os.path.exists(folder):
                try:
                    shutil.rmtree(folder)
                except Exception as e:
                    log(f"删除临时文件失败 {folder}: {e}")

    # 创建输出目录
    try:
        os.makedirs(output_dir, exist_ok=True)
        os.makedirs(os.path.join(output_dir, "tempimages"), exist_ok=True)
        os.makedirs(os.path.join(output_dir, "tempaudios"), exist_ok=True)

        log("1. 图片转换...")
        tmp_media_folder = os.path.join(output_dir, "tempimages")
        background_image_path = r"C:\Users\hexq\PycharmProjects\mypy\video\qiheqixiangjv\背景图片1.jpg"
        change_pinture(media_folder, tmp_media_folder, background_image_path)
        log("✅ 图片转换完成")

        # 文字转语音
        log("2. 文字转语音...")
        audio_dir = os.path.join(output_dir, "tempaudios")
        text_path = picname2text(tmp_media_folder)
        audio_files = text_to_audio(text_path, audio_dir)
        log("✅ 文字转语音完成")



        # 读取图片
        log("3. 图片转视频并合成视频中，请耐心等待...")
        media_path = get_media_files(tmp_media_folder)
        output_video = os.path.join(output_dir, f"video_{datetime.now().strftime('%Y%m%d%H%M%S')}.mp4")
        create_video_from_images_and_audios(text_path, media_path, audio_files, output_video, title_content, pause_duration)
        log("✅ 视频合成完成")

        # 视频合成完成后删除临时文件夹
        cleanup_temp_files(tmp_media_folder, audio_dir)

    except Exception as e:
        log(f"❌ 出错: {e}")

# if __name__ == "__main__":
#     media_folder = r"C:\Users\hexq\PycharmProjects\mypy\video\qiheqixiangjv\images"
#     output_dir = r"C:\Users\hexq\PycharmProjects\mypy\video\qiheqixiangjv\outvideo"
#     output_video = os.path.join(output_dir, f"video_{datetime.now().strftime('%Y%m%d%H%M%S')}.mp4")
#     speed_text = r"C:\Users\hexq\PycharmProjects\mypy\video\qiheqixiangjv\speed_text.txt"
#     pause = 2.0  # 每页结束后暂停时间，可调整
#     title_content = "水电费水电费递四方速递"
#
#     main_gui( media_folder, output_dir, title_content, pause)


# GUI 部分
# 定义一个log函数，用于在log_text文本框中插入消息
def log(msg):
    # 在log_text文本框中插入消息，并在末尾添加换行符
    log_text.insert(tk.END, msg + "\n")
    # 将光标移动到文本框的末尾
    log_text.see(tk.END)

# 定义选择文本文件函数
def select_text_file():
    # 弹出文件选择对话框，只允许选择文本文件
    path = filedialog.askopenfilename(filetypes=[("Text files", "*.txt")])
    # 如果选择了文件
    if path:
        # 将文件路径设置到文本框中
        text_path_var.set(path)

# 定义一个函数，用于选择媒体文件夹
def select_media_folder():
    # 弹出一个文件夹选择对话框，返回选择的文件夹路径
    path = filedialog.askdirectory()
    # 如果选择了文件夹
    if path:
        # 将选择的文件夹路径赋值给media_folder_var变量
        media_folder_var.set(path)

# 定义一个函数，用于选择输出文件夹
def select_output_folder():
    # 弹出一个文件夹选择对话框，返回选择的文件夹路径
    path = filedialog.askdirectory()
    # 如果选择了文件夹
    if path:
        # 将选择的文件夹路径赋值给output_folder_var变量
        output_folder_var.set(path)

def clear_selectiona():
    text_path_var.set("")
def clear_selectionb():
    media_folder_var.set("")
def clear_selectionc():
    output_folder_var.set("")

def clear_overview():
    overview.delete("1.0", "end")
def clear_summary():
    summary.delete("1.0", "end")
def clear_effective():
    effective.delete("1.0", "end")

overview_content = ""
summary_content = ""
effective_content = ""

media_listbox = None
preview_label = None


def show_media_files():
    global media_listbox
    global preview_label
    folder = media_folder_var.get()
    if not os.path.isdir(folder):
        messagebox.showerror("错误", "媒体文件夹不存在！")
        return

    media_files = get_media_files(folder)

    if not media_files:
        messagebox.showinfo("提示", "未找到支持的媒体文件")
        return

    # 清空原来的列表
    media_listbox.delete(0, tk.END)

    # 插入媒体文件名到列表框
    for file in media_files:
        media_listbox.insert(tk.END, os.path.basename(file))

    # 更新预览框
    def on_preview(event=None):
        selected_index = media_listbox.curselection()
        if not selected_index:
            return
        selected_file = media_files[selected_index[0]]
        ext = os.path.splitext(selected_file)[1].lower()

        if ext in [".jpg", ".jpeg", ".png"]:
            img = Image.open(selected_file)
            img.thumbnail((250, 250))
            photo = ImageTk.PhotoImage(img)
            preview_label.config(image=photo, text="")
            preview_label.image = photo  # 防止被垃圾回收
        elif ext in [".mp4", ".avi", ".mov"]:
            # 可用 ffmpeg 提取第一帧作为预览图
            preview_label.config(text="视频暂不支持封面预览")
        else:
            preview_label.config(text="不支持的格式")
    #绑定选择事件为预览
    media_listbox.bind("<<ListboxSelect>>", on_preview)
    #调用右键菜单
    create_context_menu(media_listbox)

#右键菜单
def create_context_menu(listbox):
    context_menu = tk.Menu(window, tearoff=0)
    context_menu.add_command(label="重命名", command=lambda: on_rename(listbox))
    context_menu.add_command(label="删除", command=lambda: on_delete(listbox))

    def show_context_menu(event):
        index = listbox.nearest(event.y)
        if index is not None:
            listbox.selection_clear(0, tk.END)
            listbox.selection_set(index)
            listbox.activate(index)

            try:
                # 弹出菜单，传入正确的 x 和 y 坐标
                context_menu.tk_popup(event.x_root, event.y_root)
            finally:
                context_menu.grab_release()

    # 绑定右键点击事件
    listbox.bind("<Button-3>", show_context_menu)  # 右键为<Button-3>（Tkinter 中）

def on_delete(listbox):
    selected_index = listbox.curselection()
    if not selected_index:
        return
    selected_file = listbox.get(selected_index)
    full_path = os.path.join(media_folder_var.get(), selected_file)

    # 添加确认对话框
    confirm = messagebox.askyesno("确认删除", f"你确定要删除文件 '{selected_file}' 吗？")
    if not confirm:
        return  # 用户点击“否”则退出函数
    try:
        os.remove(full_path)
        listbox.delete(selected_index)
        preview_label.config(image='', text='已删除')
        messagebox.showinfo("成功", f"已删除：{selected_file}")
    except Exception as e:
        messagebox.showerror("错误", f"无法删除文件：{e}")

def on_rename(listbox):
    selected_index = listbox.curselection()
    if not selected_index:
        return
    old_name = listbox.get(selected_index)
    full_path = os.path.join(media_folder_var.get(), old_name)

    if not os.path.exists(full_path):
        messagebox.showerror("错误", "文件不存在")
        return

    new_name = simpledialog.askstring("重命名", "请输入新文件名（不带扩展名）:", initialvalue=os.path.splitext(old_name)[0])
    if not new_name:
        return

    ext = os.path.splitext(old_name)[1]
    new_path = os.path.join(os.path.dirname(full_path), new_name + ext)

    try:
        os.rename(full_path, new_path)
        listbox.delete(selected_index)
        listbox.insert(selected_index, os.path.basename(new_path))
        listbox.selection_set(selected_index)
        messagebox.showinfo("成功", f"已重命名为：{new_name + ext}")
    except Exception as e:
        messagebox.showerror("错误", f"无法重命名：{e}")

def submit_overview():
    global overview_content
    overview_content = overview.get("1.0", tk.END).strip()
    messagebox.showinfo("已提交")
def submit_summary():
    global summary_content
    summary_content = overview.get("1.0", tk.END).strip()
    messagebox.showinfo("已提交")
def submit_effective():
    global effective_content
    effective_content = overview.get("1.0", tk.END).strip()
    messagebox.showinfo("已提交")

def start_processing():
    # 获取媒体文件夹路径
    media_folder = media_folder_var.get()
    # 获取输出文件夹路径
    output_dir = output_folder_var.get()
    # 获取暂停时间
    pause_duration = float(pause_var.get())
    # 获取封面标题
    title_content = title_var.get()

    # 判断媒体文件夹是否存在
    if not os.path.isdir(media_folder):
        # 弹出错误提示框
        messagebox.showerror("错误", "媒体文件夹不存在！")
        return
    # 判断输出目录
    if not os.path.isdir(output_dir):
        # 弹出错误提示框
        messagebox.showerror("错误", "请选择视频存放目录！")
        return
    # 是否填写封面标题
    if not title_content:
        # 弹出错误提示框
        messagebox.showerror("错误", "请填写封面标题！")
        return

    # 创建线程，调用main_gui函数
    thread = threading.Thread(
        target=main_gui, args=(
        media_folder,
        output_dir,
        title_content,
        pause_duration
        )
    )
    # 启动线程
    thread.start()

# 创建主窗口
window = tk.Tk()
window.title("复盘视频生成工具")
window.geometry("1000x800")
window.resizable(width=False, height=False)
# 配置列权重，使窗口可拉伸
# window.grid_columnconfigure(0, weight=1)
# window.grid_columnconfigure(1, weight=2)
# window.grid_columnconfigure(2, weight=2)
# window.grid_rowconfigure(0, weight=1)

# 封面标题
tk.Label(window, text="复盘封面标题:").grid(row=0, column=0, sticky="e", padx=10, pady=5)
title_var = tk.StringVar()
tk.Entry(window, textvariable=title_var, bg="lightblue", width=50).grid(row=0, column=1, sticky="w", padx=10, pady=5)

# 过程概述
tk.Label(window, text="过程概述:").grid(row=1, column=0, sticky="e", padx=10, pady=5)
overview = tk.Text(window, bg="lightyellow",height=5, width=50)
overview.grid(row=1, column=1, columnspan=2, sticky="w", padx=10, pady=5)
ovscrollbar = tk.Scrollbar(window, orient="vertical", command=overview.yview)
overview.config(yscrollcommand=ovscrollbar.set)
ovscrollbar.grid(row=1, column=1,  sticky="ens", padx=10, pady=5)
tk.Button(window, text="提交", command=submit_overview).grid(row=1, column=2, ipadx=15, padx=10, pady=5)
tk.Button(window, text="清空", command=clear_overview).grid(row=1, column=3, ipadx=15, padx=10, pady=5)
# 媒体文件夹
tk.Label(window, text="过程图片和视频文件夹:").grid(row=2, column=0, sticky="e", padx=10, pady=5)
media_folder_var = tk.StringVar()    # 媒体目录
tk.Entry(window, textvariable=media_folder_var, width=50).grid(row=2, column=1, padx=10, pady=5)
tk.Button(window, text="选择", command=select_media_folder).grid(row=2, column=2, ipadx=15, padx=10, pady=5)
tk.Button(window, text="清除", command=clear_selectionb).grid(row=2, column=3, ipadx=15, padx=10, pady=5)
# 输出目录
tk.Label(window, text="视频输出目录:").grid(row=3, column=0, sticky="e", padx=10, pady=5)
output_folder_var = tk.StringVar()     # 输出目录
tk.Entry(window, textvariable=output_folder_var, width=50).grid(row=3, column=1, padx=10, pady=5)
tk.Button(window, text="选择", command=select_output_folder).grid(row=3, column=2, ipadx=15, padx=10, pady=5)
tk.Button(window, text="清除", command=clear_selectionc).grid(row=3, column=3, ipadx=15, padx=10, pady=5)

# 暂停时间
tk.Label(window, text="每页暂停时间（秒）:").grid(row=4, column=0, sticky="e", padx=10, pady=5)
pause_var = tk.StringVar(value="2.0")    # 暂停时间
tk.Entry(window, textvariable=pause_var, width=10).grid(row=4, column=1, sticky="w", padx=10, pady=5)

#查看媒体列表
tk.Button(window, text="查看图片和视频列表", command=show_media_files, width=20, bg="green", fg="white").grid(row=5, column=1,sticky="w",  pady=20)
# 开始按钮
tk.Button(window, text="生成视频", command=start_processing, width=20, bg="green", fg="white").grid(row=5, column=2, pady=10)
# === 媒体列表区域 ===
tk.Label(window, text="图片和视频列表:").grid(row=6, column=0, sticky="w", padx=10, pady=5)
# 媒体列表框
media_listbox = tk.Listbox(window, selectmode=tk.SINGLE, height=15, width=30)
scrollbar = tk.Scrollbar(window, orient="vertical", command=media_listbox.yview)
media_listbox.config(yscrollcommand=scrollbar.set)
media_listbox.grid(row=7, column=0, sticky="w", padx=10, pady=5)
scrollbar.grid(row=7, column=0, sticky="ens", padx=10, pady=5)
# === 预览区域 ===
preview_label = tk.Label(window, text="预览区", justify="center", width=30, height=10, bg="lightgray")
preview_label.grid(row=7, column=1,  sticky="nsew", padx=10, pady=5)
# 日志输出框
tk.Label(window, text="日志:").grid(row=6, column=2,  padx=5, sticky="sw")
log_text = ScrolledText(window, width=40, height=20)
log_text.grid(row=7, column=2, columnspan=2, padx=10, pady=5)
# 总结
tk.Label(window, text="过程总结:").grid(row=8, column=0, sticky="e", padx=10, pady=5)
summary = tk.Text(window,bg="lightyellow", height=5, width=50)
summary.grid(row=8, column=1, sticky="w", padx=10, pady=5)
ovscrollbar = tk.Scrollbar(window, orient="vertical", command=summary.yview)
summary.config(yscrollcommand=ovscrollbar.set)
ovscrollbar.grid(row=8, column=1, sticky="ens", padx=10, pady=5)
tk.Button(window, text="提交", command=submit_summary).grid(row=8, column=2, ipadx=15, padx=10, pady=5)
tk.Button(window, text="清空", command=clear_summary).grid(row=8, column=3, ipadx=15, padx=10, pady=5)
# 效益分析
tk.Label(window, text="效益分析:").grid(row=9, column=0, sticky="e", padx=10, pady=5)
effective = tk.Text(window, bg="lightyellow", height=5, width=50)
effective.grid(row=9, column=1, sticky="w", padx=10, pady=5)
ovscrollbar = tk.Scrollbar(window, orient="vertical", command=effective.yview)
effective.config(yscrollcommand=ovscrollbar.set)
ovscrollbar.grid(row=9, column=1, sticky="ens", padx=10, pady=5)
tk.Button(window, text="提交", command=submit_effective).grid(row=9, column=2, ipadx=15, padx=10, pady=5)
tk.Button(window, text="清空", command=clear_effective).grid(row=9, column=3, ipadx=15, padx=10, pady=5)

window.mainloop()
